package com.linjacy.httpproject.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.linjacy.httpproject.entity.Item;
import com.linjacy.httpproject.entity.PreviousNodeDO;
import com.linjacy.httpproject.entity.User;
import com.linjacy.httpproject.exception.SystemErrorException;
import com.linjacy.httpproject.mapper.ItemMapper;
import com.linjacy.httpproject.mapper.PreviousNodeMapper;
import com.linjacy.httpproject.mapper.UserMapper;
import com.linjacy.httpproject.service.ItemService;
import com.linjacy.httpproject.service.SyncService;
import com.linjacy.httpproject.service.UserService;
import com.linjacy.httpproject.util.TransactionUtils;
import lombok.SneakyThrows;
import org.checkerframework.checker.units.qual.A;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {

    private static final Logger logger = LoggerFactory.getLogger(ItemServiceImpl.class);

    @Autowired
    ItemMapper itemMapper;
    @Autowired
    PreviousNodeMapper previousNodeMapper;
    @Autowired
    SyncService syncService;
    @Autowired
    UserMapper userMapper;
    @Autowired
    TransactionUtils transactionUtils;
    @Autowired
    Executor myTaskExecutor;

    @Override
    @SneakyThrows
    public TreeMap<String, Item> getItemSta(String date) {

        List<Item> items = itemMapper.selectList(new QueryWrapper<Item>());
        Optional.ofNullable(items).orElseThrow(()-> new SystemErrorException("并无统计数据"));

        if(items.size() <= 0){
            throw new SystemErrorException("并无统计数据");
        }

        Date parseDate = new SimpleDateFormat("yyyy-MM-dd").parse(date);
        TreeMap<String, Item> collect = items.stream().collect(Collectors.groupingBy(Item::getItemCode, TreeMap::new,
            Collectors.collectingAndThen(Collectors.toList(), list -> {
                Item item = new Item();
                item.setItemAmt(BigDecimal.ZERO);
                for (Item item1 : list) {
                    if(item1.getItemScopeDate().after(parseDate)){
                        item.setItemAmt(item.getItemAmt().add(item1.getItemAmt()));
                        item.setItemCount(item.getItemCount() + 1);
                    }
                }
                return item;
            }))
        );

        BigDecimal sum = BigDecimal.ZERO;
        for (String key : collect.keySet()) {
            sum = sum.add(collect.get(key).getItemAmt());
        }

        Item sumItem = new Item();
        collect.put("合计", sumItem.setItemAmt(sum));
        return collect;
    }

    @Override
    public Object testGroup() {
        List<PreviousNodeDO> previousNodeList = previousNodeMapper.selectList(new QueryWrapper<>());
        Map<String, List<PreviousNodeDO>> collect = previousNodeList.stream().collect(Collectors.groupingBy(PreviousNodeDO::getItemId));
        return collect;
    }

    @Override
    @Async("myTaskExecutor")
    //@DSTransactional
    //@DS("db1")
    @Transactional(rollbackFor = Exception.class)
    public void subwayBuildPlan() {
        Consumer consumer = new Consumer<String>(){
            @SneakyThrows
            @Override
            public void accept(String userId) {
                logger.info(userId);
                CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
                AtomicBoolean isError = new AtomicBoolean(false);
                // 手动开启事务
                CompletableFuture<Void> firstThread = CompletableFuture.runAsync(() -> {
                    TransactionStatus transactionFirstThread = transactionUtils.begin(TransactionDefinition.ISOLATION_READ_COMMITTED);
                    try {
                        User user = new User();
                        user.setUserId("7");
                        user.setUserName("FirstThread");
                        userMapper.insert(user);
                        //等待其余线程
                        cyclicBarrier.await();
                        if(isError.get()){
                            transactionUtils.rollback(transactionFirstThread);
                            return;
                        }
                        transactionUtils.commit(transactionFirstThread);
                    } catch (Exception e) {
                        isError.set(true);
                    }
                }, myTaskExecutor);

                CompletableFuture<Void> secondThread = CompletableFuture.runAsync(() -> {
                    TransactionStatus transactionSecondThread = transactionUtils.begin(TransactionDefinition.ISOLATION_READ_COMMITTED);
                    try {
                        User user = new User();
                        user.setUserId("8");
                        user.setUserName("SecondThread");
                        userMapper.insert(user);
                        //等待其余线程
                        cyclicBarrier.await();
                        if(isError.get()){
                            transactionUtils.rollback(transactionSecondThread);
                            return;
                        }
                        transactionUtils.commit(transactionSecondThread);
                    } catch (Exception e) {
                        isError.set(true);
                    }
                }, myTaskExecutor);

                try {
                    //阻塞等待
                    CompletableFuture.allOf(firstThread,secondThread).get();
                    User user = new User();
                    user.setUserId("9");
                    user.setUserName("MainThread");
                    userMapper.insert(user);
                }catch (Exception e){
                    isError.set(true);
                }finally {
                    if(isError.get()){
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    }
                }
            }
        };
        syncService.startSyncTask(consumer);
    }
}
